
#include <stdlib.h>
#include "../basic/operator.h"

Cell* make_integer(Scheme *sc, long num) {
	Cell* x = make_cell(sc, &g_nil, &g_nil);
	(x)->_type = INTEGER_TYPE;
	(x)->_longValue = num;
	return (x);
}

Cell* op_make_integer(Scheme *sc) {
	Cell* x = first(sc->args);
	TYPE_CHECK(sc, x, INTEGER_TYPE);
	return x;
}

// 两数求余
static long num_rem(long e1, long e2) {
	long result = e1 % e2;
	//余数必须和第一个操作数符号相同
	if ((result > 0) && (e1 < 0))
		result -= labs(e2);
	else if ((result < 0) && (e1 > 0))
		result += labs(e2);
	return result;
}

// 两数求模
static long num_mod(long e1, long e2) {
	long result = e1 % e2;
	//模数必须和第二个操作数符号相同
	if (result * e2 < 0) {
		result += e2;
	}
	return result;
}

// (quotient x y ...)
Cell* op_quotient(Scheme *sc) {
	long value;
	Cell* x;
	if (cdr(sc->args) == &g_nil) {
		x = sc->args;
		value = 1;
	} else {
		x = cdr(sc->args);
		TYPE_CHECK(sc, car(sc->args), INTEGER_TYPE);
		value = car(sc->args)->_longValue;
	}
	for (; x != &g_nil; x = cdr(x)) {
		TYPE_CHECK(sc, car(sc->args), INTEGER_TYPE);
		if (car(x)->_longValue != 0)
			value /= (car(x)->_longValue);
		else {
			sc->returnValue = make_default_exception(sc, "DIVISION_BY_ZERO", "quotient: division by zero.");
			return pop_callstack(sc);
		}
	}
	sc->returnValue = make_integer(sc, value);
	return pop_callstack(sc);
}

// (remainder x y)
Cell* op_rem(Scheme *sc) {
	Cell* x = first(sc->args);
	Cell* y = second(sc->args);
	TYPE_CHECK(sc, x, INTEGER_TYPE);
	TYPE_CHECK(sc, y, INTEGER_TYPE);

	long value = x->_longValue;
	if (y->_longValue != 0) {
		value = num_rem(value, y->_longValue);
		sc->returnValue = make_integer(sc, value);
	} else {
		sc->returnValue = make_default_exception(sc, "DIVISION_BY_ZERO", "remainder: division by zero.");
	}
	return pop_callstack(sc);
}

// (modulo x y)
Cell* op_mod(Scheme *sc) {
	Cell* x = first(sc->args);
	Cell* y = second(sc->args);
	TYPE_CHECK(sc, x, INTEGER_TYPE);
	TYPE_CHECK(sc, y, INTEGER_TYPE);

	long value = x->_longValue;
	if (y->_longValue != 0) {
		value = num_mod(value, y->_longValue);
		sc->returnValue = make_integer(sc, value);
	} else {
		sc->returnValue = make_default_exception(sc, "DIVISION_BY_ZERO", "modulo: division by zero.");
	}
	return pop_callstack(sc);
}

int divide_exactly(Cell* x, Cell* y) {
	return ((x->_longValue) % (y->_longValue) == 0);
}

Cell* int_add(Scheme *sc, Cell* x, Cell* y) {
    TYPE_CHECK_FOR_CELL(sc, x, INTEGER_TYPE);
    TYPE_CHECK_FOR_CELL(sc, y, INTEGER_TYPE);
	return make_integer(sc, (x->_longValue) + (y->_longValue));
}

Cell* int_mul(Scheme *sc, Cell* x, Cell* y) {
    TYPE_CHECK_FOR_CELL(sc, x, INTEGER_TYPE);
    TYPE_CHECK_FOR_CELL(sc, y, INTEGER_TYPE);
	return make_integer(sc, (x->_longValue) * (y->_longValue));
}

Cell* int_sub(Scheme *sc, Cell* x, Cell* y) {
    TYPE_CHECK_FOR_CELL(sc, x, INTEGER_TYPE);
    TYPE_CHECK_FOR_CELL(sc, y, INTEGER_TYPE);
	return make_integer(sc, (x->_longValue) - (y->_longValue));
}

Cell* int_div(Scheme *sc, Cell* x, Cell* y) {
    TYPE_CHECK_FOR_CELL(sc, x, INTEGER_TYPE);
    TYPE_CHECK_FOR_CELL(sc, y, INTEGER_TYPE);
	if (is_zero_number(y)) {
		return make_default_exception(sc, "DIVISION_BY_ZERO", "/: division by zero.");
	}
	if (divide_exactly(x, y)) {
		return make_integer(sc, (x->_longValue) / (y->_longValue));
	} else {
		return make_fraction(sc, make_integer(sc, x->_longValue), make_integer(sc, y->_longValue));
	}
}

Cell* int_to_fraction(Scheme *sc, Cell* x) {
    TYPE_CHECK_FOR_CELL(sc, x, INTEGER_TYPE);
	return make_fraction(sc, x, make_integer(sc, 1));
}

Cell* int_to_decimal(Scheme *sc, Cell* x) {
    TYPE_CHECK_FOR_CELL(sc, x, INTEGER_TYPE);
	return make_decimal(sc, (double)(x->_longValue));
}

void init_integer_operators(Scheme *sc) {
	ADD_OPERATOR(sc, "make-integer", make_integer);
	ADD_OPERATOR(sc, "quotient", quotient);
	ADD_OPERATOR(sc, "remainder", rem);
	ADD_OPERATOR(sc, "modulo", mod);
}
